import express, { Express } from 'express'
import ejs from 'ejs'
import { ModelMeta, PathMeta } from './Meta'
import { setSwaggerJSONByModelMeta, setSwaggerJSONByPathMeta } from './json'
import path from 'path'
import redis from '../redis'

export * from './decorator'
export * from './DataType'
export * from './Meta'
export { DataFormat } from '../router'

type Schemes = ('http' | 'https')[]

interface SwaggerOptions {
  title?: string
  description?: string
  version?: string
  host?: string
  basePath?: string
  schemes?: Schemes
  apiKey?: string
  defaultResponseModel?: string
}

interface SwaggerJSON {
  [key: string]: any
  swagger?: string
  info?: { [key: string]: any }
  tags?: ModelMeta[]
  schemes?: Schemes
  paths?: { [key: string]: any }
  securityDefinitions?: { [key: string]: any }
  security?: any[]
  definitions?: { [key: string]: any }
  host?: string
  basePath?: string
}

const DefaultSwaggerOptions: SwaggerOptions = {
  title: 'Swagger 接口文档',
  description: '',
  version: '1.0.0',
  host: '127.0.0.1',
  basePath: '/',
  schemes: ['http', 'https'],
  apiKey: 'Authorization'
}

class Swagger {
  static SwaggerJson: SwaggerJSON = null

  // 默认数据响应格式 model
  static DefaultResponseModel = ''

  static ModelMetas: ModelMeta[] = []

  static PathMetas: PathMeta[] = []

  static REDIS_SWAGGER_JSON = 'REDIS_SWAGGER_JSON'

  static IS_SAVE_REDIS = false


  constructor(app: Express, entry: string, options: SwaggerOptions) {
    if (options.defaultResponseModel) {
      Swagger.DefaultResponseModel = options.defaultResponseModel
    }

    if (!entry.startsWith('/')) {
      throw new Error('swagger 入口路径必须 / 开头')
    }

    // 将收集的数据处理成 swagger json
    const opts = Object.assign(DefaultSwaggerOptions, options) as SwaggerOptions
    const SwaggerJSON = Swagger.getSwaggerJSON()
    SwaggerJSON.info.title = opts.title
    SwaggerJSON.info.description = opts.description
    SwaggerJSON.info.version = opts.version
    SwaggerJSON.host = opts.host
    SwaggerJSON.basePath = opts.basePath
    SwaggerJSON.schemes = opts.schemes
    SwaggerJSON.securityDefinitions[opts.apiKey] = {
      type: 'apiKey',
      name: opts.apiKey,
      in: 'header'
    }
    SwaggerJSON.security.push({ [opts.apiKey]: [] })

    Swagger.ModelMetas.forEach(modelMeta => setSwaggerJSONByModelMeta(modelMeta))

    Swagger.PathMetas.forEach(pathMeta => setSwaggerJSONByPathMeta(pathMeta))

    // 如果有连接 redis，将 swagger json 保存到 redis
    if (redis.isReady && !Swagger.IS_SAVE_REDIS) {
      redis.set(Swagger.REDIS_SWAGGER_JSON, JSON.stringify(SwaggerJSON)).then(() => {
        Swagger.clearSwaggerJSON()
        Swagger.IS_SAVE_REDIS = true
      })
    }

    const DistPath = path.resolve(__dirname, './swagger-ui-dist')

    const JsonFileName = 'swagger.json'

    const AssetsPath = entry + '/assets'

    // 默认模板文件夹 views
    app.set('views', DistPath)
    // 修改 ejs 后缀为 html
    app.engine('html', ejs.__express)
    app.set('view engine', 'html')
    // 设置模板
    app.get(entry, function (_, res) {
      res.render('index', {
        url: SwaggerJSON.host + entry + `/${JsonFileName}`,
        assetsPath: AssetsPath
      })
    })
    // 设置静态文件
    app.use(AssetsPath, express.static(DistPath))
    // 设置 swagger.json
    app.get(entry + `/${JsonFileName}`, async function (_, res) {
      try {
        if (redis.isReady && Swagger.IS_SAVE_REDIS) {
          const jsonString = await redis.get(Swagger.REDIS_SWAGGER_JSON)
          res.json(JSON.parse(jsonString))
        } else {
          throw new Error('swagger.json 没有保存到 redis')
        }
      } catch (error) {
        console.log(error.toString())
        res.json(SwaggerJSON)
      }
    })
  }

  static getSwaggerJSON(): SwaggerJSON {
    return Swagger.SwaggerJson || (Swagger.SwaggerJson = {
      swagger: '2.0',
      info: {},
      tags: [],
      schemes: [],
      paths: {},
      securityDefinitions: {},
      security: [],
      definitions: {}
    } as SwaggerJSON)
  }

  static clearSwaggerJSON() {
    Swagger.SwaggerJson = null
  }
}

export default Swagger